package com.example.miaosh.util;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * -常用加密算法工具类 <br>
 * 
 * -包括:
 * <li>AES
 * <li>Base64
 * <li>MD5
 * 
 * @version 1.0
 */
public class EncryptKit {

	public static void main(String[] args) throws UnsupportedEncodingException {
		/**
		 * aes加密测试
		 */
		String content = "美女，约吗？";
		String password = "155154ddadd";
		System.out.println("加密之前：" + content);
		// 如果想要加密内容不显示乱码，可以先将密文转换为16进制
		String hexStrResult = AES.encrypt(content, password);
		System.out.println("16进制的密文：" + hexStrResult);
		String decrypt = AES.decrypt("38169CEA5F25702FA6440710F039A2228BFD65E87AF13038CB47F16085483D85", password);
		System.out.println("解密后的内容：" + decrypt);

		/**
		 * base64测试
		 */
		String str = "美女，约吗?";
		String o1 = Base64.encode(str.getBytes());
		String o2 = Base64.encode(str);
		String o3 = Base64.encode(str, "utf-8");
		String o4 = Base64.encode(str, "UTf-8");

		String o5 = Base64.decodeToStr(o4, "UTf-8");
		String o6 = Base64.decodeToStr(o4);
		System.out.println("o1:" + o1);
		System.out.println("o2:" + o2);
		System.out.println("o3:" + o3);
		System.out.println("o4:" + o4);
		System.out.println("o5:" + o5);
		System.out.println("o6:" + o6);

		String o7 = MD5.MD5Encode(str, "utf-8");
		System.out.println(o7);

		String o8 = Hash.generateSalt(5);
		Long o9 = Hash.fnv1a64("123");
		System.out.println(o8);
		System.out.println(o9);
		System.out.println(Arrays.toString(HexBinDecOct.parseHexStr2Byte("10")));

	}

	/**
	 * -AES 加密方式<br>
	 * 
	 * @author Gray-bird
	 * @version 1.0
	 *
	 */
	public static class AES {
		/**
		 * AES加密字符串
		 * 
		 * @param content
		 *            需要被加密的字符串
		 * @param password
		 *            加密需要的密码
		 * @return 16进制密文
		 */
		public static String encrypt(String content, String password) {
			try {
				KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者
				kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出
				SecretKey secretKey = kgen.generateKey();// 根据用户密码，生成一个密钥
				byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥，如果此密钥不支持编码，则返回
				SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
				Cipher cipher = Cipher.getInstance("AES");// 创建密码器
				byte[] byteContent = content.getBytes("utf-8");
				cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器
				byte[] result = cipher.doFinal(byteContent);// 加密
				return HexBinDecOct.parseByte2HexStr(result);

			} catch (NoSuchPaddingException e) {
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			} catch (InvalidKeyException e) {
				e.printStackTrace();
			} catch (IllegalBlockSizeException e) {
				e.printStackTrace();
			} catch (BadPaddingException e) {
				e.printStackTrace();
			}
			return null;
		}

		/**
		 * 解密AES加密过的字符串
		 * 
		 * @param content
		 *            AES加密过过的内容,16进制字符串
		 * @param password
		 *            加密时的密码
		 * @return 明文
		 */
		public static String decrypt(String hexStrResult, String password) {
			try {
				byte[] twoStrResult = HexBinDecOct.parseHexStr2Byte(hexStrResult);
				KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者
				kgen.init(128, new SecureRandom(password.getBytes()));
				SecretKey secretKey = kgen.generateKey();// 根据用户密码，生成一个密钥
				byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥
				SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
				Cipher cipher = Cipher.getInstance("AES");// 创建密码器
				cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器
				byte[] result = cipher.doFinal(twoStrResult);
				return new String(result);
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
			} catch (NoSuchPaddingException e) {
				e.printStackTrace();
			} catch (InvalidKeyException e) {
				e.printStackTrace();
			} catch (IllegalBlockSizeException e) {
				e.printStackTrace();
			} catch (BadPaddingException e) {
				e.printStackTrace();
			}
			return null;
		}
	}

	/**
	 * -Base64 编码方式
	 * 
	 * @author Gray-bird
	 *
	 */
	public static class Base64 {
		public static final Charset UTF_8 = Charset.forName("UTF-8");
		private static IBase64 delegate;

		private Base64() {

		}

		private static boolean isPresent(String className, ClassLoader classLoader) {
			try {
				Class.forName(className, true, classLoader);
				return true;
			} catch (Throwable ex) {
				return false;
			}
		}

		static {
			if (isPresent("java.util.Base64", Base64.class.getClassLoader())) {
				delegate = new Java8Base64();
			} else {
				delegate = new Java67Base64();
			}
		}

		/**
		 * 编码
		 * 
		 * @param value
		 *            byte数组
		 * @return {String}
		 */
		public static String encode(byte[] value) {
			return delegate.encode(value);
		}

		/**
		 * 编码
		 * 
		 * @param value
		 *            字符串
		 * @return {String}
		 */
		public static String encode(String value) {
			byte[] val = value.getBytes(UTF_8);
			return delegate.encode(val);
		}

		/**
		 * 编码
		 * 
		 * @param value
		 *            字符串
		 * @param charsetName
		 *            charSet
		 * @return {String}
		 */
		public static String encode(String value, String charsetName) {
			byte[] val = value.getBytes(Charset.forName(charsetName));
			return delegate.encode(val);
		}

		/**
		 * 解码
		 * 
		 * @param value
		 *            字符串
		 * @return {byte[]}
		 */
		public static byte[] decode(String value) {
			return delegate.decode(value);
		}

		/**
		 * 解码
		 * 
		 * @param value
		 *            字符串
		 * @return {String}
		 */
		public static String decodeToStr(String value) {
			byte[] decodedValue = delegate.decode(value);
			return new String(decodedValue, UTF_8);
		}

		/**
		 * 解码
		 * 
		 * @param value
		 *            字符串
		 * @param charsetName
		 *            字符集
		 * @return {String}
		 */
		public static String decodeToStr(String value, String charsetName) {
			byte[] decodedValue = delegate.decode(value);
			return new String(decodedValue, Charset.forName(charsetName));
		}

		private static interface IBase64 {
			public String encode(byte[] value);

			public byte[] decode(String value);
		}

		private static class Java8Base64 implements IBase64 {
			@Override
			public String encode(byte[] value) {
				return java.util.Base64.getEncoder().encodeToString(value);
			}

			@Override
			public byte[] decode(String value) {
				return java.util.Base64.getDecoder().decode(value);
			}
		}

		private static class Java67Base64 implements IBase64 {
			public String encode(byte[] data) {
				return javax.xml.bind.DatatypeConverter.printBase64Binary(data);
			}

			public byte[] decode(String base64) {
				return javax.xml.bind.DatatypeConverter.parseBase64Binary(base64);
			}
		}
	}

	/**
	 * -MD5 加密方式
	 * 
	 * @author Gray-bird
	 *
	 */
	public static class MD5 {
		/**
		 * Md5加密
		 * 
		 * @param str
		 * @param salt
		 *            钥匙 增强加密型，
		 * @return
		 */
		private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c",
				"d", "e", "f" };

		private static String byteArrayToHexString(byte b[]) {
			StringBuffer resultSb = new StringBuffer();
			for (int i = 0; i < b.length; i++)
				resultSb.append(byteToHexString(b[i]));
			return resultSb.toString();
		}

		private static String byteToHexString(byte b) {
			int n = b;
			if (n < 0)
				n += 256;
			int d1 = n / 16;
			int d2 = n % 16;
			return hexDigits[d1] + hexDigits[d2];
		}

		/**
		 * md5加密
		 * 
		 * @param str
		 *            带加密字符串
		 * @param charsetname
		 *            编码格式
		 * @return
		 */
		public static String MD5Encode(String str, String charsetname) {
			String resultString = null;
			try {
				resultString = new String(str);
				MessageDigest md = MessageDigest.getInstance("MD5");
				if (charsetname == null || "".equals(charsetname))
					resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
				else
					resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return resultString;
		}
	}

	/**
	 * hash类
	 * 
	 * @author wangshoukui
	 *
	 */
	public static class Hash {
		public static final long FNV_OFFSET_BASIS_64 = 0xcbf29ce484222325L;
		public static final long FNV_PRIME_64 = 0x100000001b3L;
		private static final java.security.SecureRandom random = new java.security.SecureRandom();
		private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
		private static final char[] CHAR_ARRAY = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
				.toCharArray();

		public static long fnv1a64(String key) {
			long hash = FNV_OFFSET_BASIS_64;
			for (int i = 0, size = key.length(); i < size; i++) {
				hash ^= key.charAt(i);
				hash *= FNV_PRIME_64;
			}
			return hash;
		}

		public static String md5(String srcStr) {
			return hash("MD5", srcStr);
		}

		public static String sha1(String srcStr) {
			return hash("SHA-1", srcStr);
		}

		public static String sha256(String srcStr) {
			return hash("SHA-256", srcStr);
		}

		public static String sha384(String srcStr) {
			return hash("SHA-384", srcStr);
		}

		public static String sha512(String srcStr) {
			return hash("SHA-512", srcStr);
		}

		public static String hash(String algorithm, String srcStr) {
			try {
				MessageDigest md = MessageDigest.getInstance(algorithm);
				byte[] bytes = md.digest(srcStr.getBytes("utf-8"));
				return toHex(bytes);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		public static String toHex(byte[] bytes) {
			StringBuilder ret = new StringBuilder(bytes.length * 2);
			for (int i = 0; i < bytes.length; i++) {
				ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
				ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
			}
			return ret.toString();
		}

		/**
		 * md5 128bit 16bytes sha1 160bit 20bytes sha256 256bit 32bytes sha384 384bit
		 * 48bytes sha512 512bit 64bytes
		 */
		public static String generateSalt(int saltLength) {
			StringBuilder salt = new StringBuilder(saltLength);
			for (int i = 0; i < saltLength; i++) {
				salt.append(CHAR_ARRAY[random.nextInt(CHAR_ARRAY.length)]);
			}
			return salt.toString();
		}

		public static String generateSaltForSha256() {
			return generateSalt(32);
		}

		public static String generateSaltForSha512() {
			return generateSalt(64);
		}

		public static boolean slowEquals(byte[] a, byte[] b) {
			if (a == null || b == null) {
				return false;
			}

			int diff = a.length ^ b.length;
			for (int i = 0; i < a.length && i < b.length; i++) {
				diff |= a[i] ^ b[i];
			}
			return diff == 0;
		}
	}

	/**
	 * 进制转换类
	 * 
	 * @author wangshoukui
	 *
	 */
	public static class HexBinDecOct {
		/**
		 * 将二进制转换成16进制
		 * 
		 * @param buf
		 * @return
		 */
		public static String parseByte2HexStr(byte buf[]) {
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < buf.length; i++) {
				String hex = Integer.toHexString(buf[i] & 0xFF);
				if (hex.length() == 1) {
					hex = '0' + hex;
				}
				sb.append(hex.toUpperCase());
			}
			return sb.toString();
		}

		/**
		 * 将16进制转换为二进制
		 * 
		 * @param hexStr
		 * @return
		 */
		public static byte[] parseHexStr2Byte(String hexStr) {
			if (hexStr.length() < 1)
				return null;
			byte[] result = new byte[hexStr.length() / 2];
			for (int i = 0; i < hexStr.length() / 2; i++) {
				int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
				int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
				result[i] = (byte) (high * 16 + low);
			}
			return result;
		}
	}

}
